<template>
  <div class="edit-value">
    <div class="edit-value__header">
      <div></div>
      <div class="option">
        <t-tooltip placement="top" :content="$t('layout.aceEditor.format')">
          <t-button theme="default" shape="square" variant="text" size="small" @click="onFormat">
            <steve-icon name="beautify" size="18px" />
          </t-button>
        </t-tooltip>
        <t-tooltip v-if="aceLang === 'json'" placement="top" :content="$t('layout.aceEditor.compress')">
          <t-button theme="default" shape="square" variant="text" size="small" @click="onCompress">
            <steve-icon name="compress" size="18px" />
          </t-button>
        </t-tooltip>
        <t-tooltip placement="top" :content="$t('layout.aceEditor.softWrap')">
          <t-button
            :theme="aceEditorConfig.wrap ? 'primary' : 'default'"
            shape="square"
            variant="text"
            size="small"
            @click="onSoftWrap"
          >
            <steve-icon name="soft-wrap" size="18px" />
          </t-button>
        </t-tooltip>
        <t-tooltip placement="top" :content="$t('layout.aceEditor.copy')">
          <t-button theme="default" shape="square" variant="text" size="small" @click="onCopySource">
            <steve-icon name="copy" size="18px" />
          </t-button>
        </t-tooltip>
      </div>
    </div>
    <v-ace-editor
      ref="aceEditorRef"
      v-model:value="aceEditorValue"
      v-model:options="aceEditorConfig"
      :lang="aceLang"
      :readonly="readonly"
      :placeholder="placeholder"
      theme="monokai"
      class="ace-editor"
      :style="getHeightStyle"
      @focus="onAceFocus"
      @blur="onAceBlur"
      @input="onAceInput"
    />
    <div class="edit-value__footer">
      <div class="position">
        <span>Line {{ cursorPosition.row + 1 }}</span>
        <span>Column {{ cursorPosition.column + 1 }}</span>
      </div>
      <div class="lang">
        <span>{{ aceLang }}</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import './ace-config';

import { useClipboard, useLocalStorage } from '@vueuse/core';
import { Ace } from 'ace-builds';
import beautify from 'js-beautify';
import { MessagePlugin } from 'tdesign-vue-next';
import { isJSON } from 'validator';
import { computed, CSSProperties, onMounted, ref, toRefs, watch } from 'vue';
import { VAceEditor } from 'vue3-ace-editor/index.js';

import steveIcon from '@/components/steveIcon/index.vue';
import { localeKeys } from '@/config/global';
import { compressJSON, formatJSON } from '@/utils/common';

const emit = defineEmits(['focus', 'blur', 'update:value']);

// props
const props = defineProps({
  // fromData
  value: {
    type: String,
    default: '',
  },
  // 组件高度
  height: {
    type: Number,
    default: 0,
  },
  // 语言
  lang: {
    type: String,
    default: '',
  },
  // 是否只读
  readonly: {
    type: Boolean,
    default: false,
  },
  placeholder: String,
});

// 获取组件高度
const computedStyle = getComputedStyle(document.documentElement);
const header = computedStyle.getPropertyValue('--td-comp-size-s');
const footer = computedStyle.getPropertyValue('--td-comp-size-xxs');
const getHeightStyle = computed((): CSSProperties => {
  let h = props.height;
  if (h === 0) {
    h = window.innerHeight;
  }
  h = h - parseFloat(header) - parseFloat(footer);
  return {
    height: `${h}px`,
  };
});

const aceEditorRef = ref<Ace.Editor>();
const cursorPosition = ref({
  row: 0,
  column: 0,
});
onMounted(() => {
  // eslint-disable-next-line no-underscore-dangle
  if (aceEditorRef.value) {
    const ae = aceEditorRef.value?._editor;
    ae.selection.on('changeCursor', () => {
      // 获取光标所在的位置
      cursorPosition.value = ae.getCursorPosition();
    });
  }
});

// 编辑中字体大小
const aceEditorFontSize = ref(14);

// JSON编辑器配置
const aceEditorValue = ref('');
const aceEditorConfig = ref({
  // 代码检查
  useWorker: true,
  // 启用代码折叠
  enableFolding: true,
  // 折叠样式，"markbegin", "markbeginend", "manual"
  foldStyle: 'markbeginend',
  wrap: useLocalStorage(localeKeys.aceEditor.wrap, true).value,
  fontSize: `${aceEditorFontSize.value}px`,
  enableBasicAutocompletion: true,
  enableLiveAutocompletion: true,
  enableSnippets: true,
});

// 监听配置变化并更新编辑器
const updateEditorConfig = (newConfig: any) => {
  aceEditorConfig.value = { ...aceEditorConfig.value, ...newConfig };
};

const { value, lang } = toRefs(props);
const aceLang = ref(lang.value || 'json');
aceEditorValue.value = value.value;
watch(value, (v) => {
  aceEditorValue.value = v;
  if (aceLang.value !== 'javascript') {
    aceLang.value = isJSON(v) ? 'json' : 'html';
  }
});

// 当Ace编辑器获取焦点事件
const onAceFocus = (content: any) => {
  emit('focus', content);
};

// 当Ace编辑器失去焦点时事件
const onAceBlur = (content: any) => {
  emit('blur', content);
};

const onAceInput = () => {
  emit('update:value', aceEditorValue.value);
};

const onFormat = () => {
  if (aceLang.value === 'json') {
    aceEditorValue.value = formatJSON(aceEditorValue.value);
  } else if (aceLang.value === 'html') {
    aceEditorValue.value = beautify.html(aceEditorValue.value);
  } else if (aceLang.value === 'javascript') {
    aceEditorValue.value = beautify.js(aceEditorValue.value);
  }
};

const onCompress = () => {
  if (aceLang.value === 'json') {
    aceEditorValue.value = compressJSON(aceEditorValue.value);
  } else if (aceLang.value === 'html') {
    console.log('不做压缩');
  }
};

// Copy
const onCopySource = () => {
  const { copy } = useClipboard({ source: aceEditorValue.value });
  copy()
    .then(() => {
      MessagePlugin.closeAll();
      MessagePlugin.success('复制成功');
    })
    .catch(() => {
      MessagePlugin.closeAll();
      MessagePlugin.error('复制失败');
    });
};

// 换行
const onSoftWrap = () => {
  const wrap = !aceEditorConfig.value.wrap;
  updateEditorConfig({ wrap });
  useLocalStorage(localeKeys.aceEditor.wrap, true).value = wrap;
};
</script>

<style lang="less" scoped>
.edit-value {
  position: relative;
  border-radius: var(--td-radius-medium);
  overflow: hidden;

  &__footer {
    background: var(--td-bg-color-component);
    display: flex;
    align-content: center;
    justify-content: space-between;
    font-size: 11px;
    padding: 0 var(--td-comp-paddingLR-s);

    span {
      margin-right: var(--td-comp-margin-s);

      &:last-child {
        margin-right: 0;
      }
    }
  }

  &__header {
    background: var(--td-bg-color-component);
    display: flex;
    align-content: center;
    justify-content: space-between;
    height: var(--td-comp-size-s);
    padding: 0 var(--td-comp-paddingLR-s);

    .option {
      display: flex;
      align-items: center;
    }
  }
}
</style>
